import threading
import time
from typing import Optional, Dict, Any, List
from collections import OrderedDict, deque
from .page import PageManager

class BufferPool:
    """
    缓冲池管理器（Buffer Pool）
    负责缓存数据页，减少磁盘I/O，支持多种缓存替换策略（如LRU、FIFO）。
    设计决策：
      - 支持策略模式，便于扩展更多缓存淘汰算法（如LRU-K、Clock等）。
      - 采用字典+有序结构，兼顾查找效率和淘汰效率。
      - 记录命中率、替换日志等关键性能指标，便于监控和调优。
      - 预留并发控制、预取、压缩等扩展点。
    注意事项：
      - 当前实现为单线程环境，若多线程需加锁保证一致性。
      - 仅支持页级缓存，未实现行/列粒度缓存。
    """

    def __init__(self, page_manager: PageManager, cache_size: int = 100, strategy: str = "LRU"):
        """
        初始化缓冲池

        Args:
            page_manager: 页管理器实例
            cache_size: 缓存大小（页数）
            strategy: 替换策略，支持"LRU"或"FIFO"
        """
        self.page_manager = page_manager
        self.cache_size = cache_size
        self.strategy = strategy.upper()
        # 缓存结构：{page_id: {"data": bytes, "dirty": bool}}
        self.cache: Dict[int, Dict[str, Any]] = {}
        # LRU策略数据结构
        self.lru_order = OrderedDict()
        # FIFO策略数据结构
        self.fifo_queue = deque()
        self.fifo_order = {}
        # 统计信息
        self.hit_count = 0
        self.miss_count = 0
        self.replace_count = 0
        # 替换日志
        self.replace_log: List[Dict[str, Any]] = []
        # 并发控制锁（如需多线程支持）
        self._lock = threading.RLock()

    def get_page(self, page_id: int) -> Optional[bytes]:
        """
        获取页面，如果页面在缓存中则直接返回，否则从磁盘读取并缓存。
        支持LRU/FIFO等策略的访问顺序维护。
        性能优化：缓存命中时仅维护访问顺序，未命中时自动淘汰最优页。
        """
        with self._lock:
            if page_id in self.cache:
                self.hit_count += 1
                if self.strategy == "LRU":
                    self.lru_order.move_to_end(page_id)
                return self.cache[page_id]["data"]

            self.miss_count += 1
            page_data = self.page_manager.read_page(page_id)
            if page_data is None:
                return None

            if len(self.cache) >= self.cache_size:
                self._replace_page()

            self.cache[page_id] = {"data": page_data, "dirty": False}
            if self.strategy == "LRU":
                self.lru_order[page_id] = True
                self.lru_order.move_to_end(page_id)
            else:  # FIFO
                self.fifo_queue.append(page_id)
                self.fifo_order[page_id] = len(self.fifo_queue) - 1
            return page_data

    def mark_dirty(self, page_id: int) -> bool:
        """
        标记页为脏页（已修改），便于后续刷盘。
        若页不在缓存中则自动加载。
        """
        with self._lock:
            if page_id not in self.cache:
                page_data = self.get_page(page_id)
                if page_data is None:
                    return False
            self.cache[page_id]["dirty"] = True
            return True

    def write_page(self, page_id: int, data: bytes) -> bool:
        """
        写入页数据到缓存，并标记为脏页。
        若缓存已满，先淘汰一页。
        """
        with self._lock:
            if page_id not in self.cache:
                if len(self.cache) >= self.cache_size:
                    self._replace_page()
                self.cache[page_id] = {"data": data, "dirty": True}
                if self.strategy == "LRU":
                    self.lru_order[page_id] = True
                    self.lru_order.move_to_end(page_id)
                else:
                    self.fifo_queue.append(page_id)
                    self.fifo_order[page_id] = len(self.fifo_queue) - 1
            else:
                self.cache[page_id]["data"] = data
                self.cache[page_id]["dirty"] = True
                if self.strategy == "LRU":
                    self.lru_order.move_to_end(page_id)
            return True

    def flush_page(self, page_id: int) -> bool:
        """
        将缓存中的页面写回磁盘（仅脏页需要写回）。
        设计决策：只刷脏页，减少I/O。
        """
        with self._lock:
            if page_id not in self.cache:
                return False
            if self.cache[page_id]["dirty"]:
                success = self.page_manager.write_page(page_id, self.cache[page_id]["data"])
                if success:
                    self.cache[page_id]["dirty"] = False
                return success
            return True

    def flush_all(self) -> None:
        """
        将所有脏页写回磁盘，保证数据持久性。
        可用于数据库关闭或检查点。
        """
        with self._lock:
            for page_id in list(self.cache.keys()):
                self.flush_page(page_id)

    def _replace_page(self):
        """
        根据替换策略淘汰一个页面，必要时写回脏页。
        支持LRU和FIFO，便于后续扩展为LRU-K、Clock等。
        设计决策：淘汰时优先刷脏页，记录淘汰日志，便于性能分析。
        """
        if self.strategy == "LRU":
            page_id, _ = self.lru_order.popitem(last=False)
        else:
            while self.fifo_queue:
                page_id = self.fifo_queue.popleft()
                if page_id in self.fifo_order and self.fifo_order[page_id] != -1:
                    del self.fifo_order[page_id]
                    break
        # 写回脏页
        if page_id in self.cache and self.cache[page_id]["dirty"]:
            self.page_manager.write_page(page_id, self.cache[page_id]["data"])
        # 记录淘汰日志
        self.replace_log.append({
            "timestamp": time.time(),
            "page_id": page_id,
            "strategy": self.strategy
        })
        # 从缓存移除
        if page_id in self.cache:
            del self.cache[page_id]
            self.replace_count += 1

    def get_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息，包括命中率、脏页数等。
        便于监控和性能调优。
        """
        total_access = self.hit_count + self.miss_count
        hit_ratio = self.hit_count / total_access if total_access > 0 else 0
        dirty_pages = sum(1 for page_info in self.cache.values() if page_info["dirty"])
        return {
            "hit_count": self.hit_count,
            "miss_count": self.miss_count,
            "hit_ratio": hit_ratio,
            "replace_count": self.replace_count,
            "cache_size": len(self.cache),
            "max_cache_size": self.cache_size,
            "dirty_pages": dirty_pages
        }

    def get_replace_log(self, limit: int = 10) -> List[Dict[str, Any]]:
        """
        获取最近的页面淘汰日志，便于性能分析和调优。
        """
        return self.replace_log[-limit:] if limit > 0 else self.replace_log
